iT邦幫忙

2024 iThome 鐵人賽

DAY 8
0
自我挑戰組

Linux Kernel 網路巡禮系列 第 8

虛擬檔案系統 VFS (2)

  • 分享至 

  • xImage
  •  

昨天,我們介紹了 Linux VFS 中幾個重要的資料結構,包括 inodedentryfile。不過,這些資料結構僅是保存在 Kernel 記憶體中,並不是實際存在於硬碟上的檔案。當我們透過 openread 等 system call 建立或讀取檔案內容時,實際上需要有檔案系統驅動去執行對硬碟的讀寫。而且, dentry 並不會一開始就存在於 Kernel 中,而是在需要的時候,讀取硬碟來得知資料夾內的實際檔案和子目錄,然後建立對應的 dentry 結構,而不是一口氣把整個硬碟的目錄載入,不然記憶體跟載入時間的成本會過高。今天,我們將探討這些過程是如何實現的。

Operations 概述

回顧我們之前提到的 network namespace 功能擴充方式,Linux 網路子系統定義了 pernet_operations 介面,讓 Kernel module 實作,當 namespace 新增或刪除時,系統會執行 Kernel module 所定義的操作函數。在 VFS 系統中,Linux 採用了類似的方式。

昨天,我們介紹了 Linux VFS 中幾個重要的資料結構,包括 inodedentryfile。但是這些都只是保存在Kernel 中的資料,並不是實際存在硬碟上的檔案。當我們透過open/read system call去建立、讀取檔案內容的時候,總要有人實際去執行這些操作。 dentry也不可能是一開始就存在在Kernel中,總是要有人去讀取硬碟,得知該資料夾內實際上有那些檔案或子目錄,然後建立對應的dentry結構。今天就要來講這些是怎麼實現的。

在 VFS 中,對每個元件都定義了對應的操作介面,如 inode_operationsfile_operationsdentry_operations 等。

// include/linux/fs.h
struct file {
    ...
    const struct file_operations	*f_op;
    ...
}

struct inode {
    ...
    const struct inode_operations	*i_op;
    ...
}

// include/linux/dcache.h
struct dentry {
    ...
    const struct dentry_operations *d_op;
    ...
}

不同於 pernet_operations 的多重註冊方式,VFS 中,每個資料結構實例只會有一組對應的操作介面。

以file_operations為例,他定義了許多對file的操作函數。

// include/linux/fs.h
struct file_operations {
	struct module *owner;
	loff_t (*llseek) (struct file *, loff_t, int);
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
	ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
	ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
	int (*iopoll)(struct kiocb *kiocb, struct io_comp_batch *,
			unsigned int flags);
	int (*iterate_shared) (struct file *, struct dir_context *);
	__poll_t (*poll) (struct file *, struct poll_table_struct *);
	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
	int (*mmap) (struct file *, struct vm_area_struct *);
	unsigned long mmap_supported_flags;
	int (*open) (struct inode *, struct file *);
	int (*flush) (struct file *, fl_owner_t id);
    ...
} __randomize_layout;

這個結構中定義了許多熟悉的操作函數名稱,如 readwriteopenmmap。當 User Space 的程式呼叫這些 system call 來操作檔案時,內核會執行對應的 file_operations。這些函數的實作則由具體的檔案系統驅動(例如 ext4、proc 等)來提供。不同的檔案系統驅動不一定會實作所有的操作函數,如果沒有定義,可能會使用系統內建的函數,或著表示不支援該操作。

https://ithelp.ithome.com.tw/upload/images/20240922/20152703Wkb9ac0FlN.png

Super Block 與檔案系統驅動

今天還要介紹另一個重要的 VFS 資料結構:super_block。在硬碟中,最小的儲存單位是 block,而硬碟的第一個 block 通常儲存硬碟的一些 metadata,例如 inode 和 block 的總數及分配狀況等。這個 block 被稱之為 super block。

在 VFS 中,super block對應到的資料結構就是 struct super_block,因此可以說,每個 super_block 結構代表一個檔案系統。

// include/linux/fs.h
struct super_block {
    ...
    unsigned long		s_blocksize;
	loff_t			s_maxbytes;	/* Max file size */
	struct file_system_type	*s_type;
	const struct super_operations	*s_op;
}

這裡需要特別提到兩個欄位。首先是 *s_op。跟其他結構一樣,super_block提供了super_operations介面:

// /include/linux/fs.h
struct super_operations {
   	struct inode *(*alloc_inode)(struct super_block *sb);
	void (*destroy_inode)(struct inode *);
	void (*free_inode)(struct inode *);
    ...
    int (*sync_fs)(struct super_block *sb, int wait);
    ...
    int (*show_devname)(struct seq_file *, struct dentry *);
    ...
}

當然 super_operations 就提供了一些針對整個檔案系統或著說硬碟的操作,當然就包含了 inode 的分配跟刪除。

另外一個欄位是 s_type,這個欄位指向 file_system_type 結構。

檔案系統驅動

file_system_type 對應檔案系統的類型,如 ext4 或 proc。當檔案系統驅動載入時,就會向Linux kernel註冊一個 file_system_type 結構。

// /include/linux/fs.h
struct file_system_type {
    const char *name; // 檔案系統名稱 ext4, ...
    int (*init_fs_context)(struct fs_context *);
    ...
    struct dentry *(*mount) (struct file_system_type *, int,
		       const char *, void *);
   ...
}

其中比較重要的是 init_fs_contextmount 這兩個函數,當我們使用 mount 去掛載一個設備時,就會呼叫到這兩個函數來初始化檔案系統,建立super_block函數,及載入檔案系統根目錄的inode跟dentry,設置superblock/inode/dentry_operations等等,具體mount怎麼去初始化檔案系統和這些結構的就超出範圍為不談了。

我們可以透過 cat /proc/filesystems 來查看系統中註冊的檔案系統。

> cat /proc/filesystems
nodev   sysfs
nodev   tmpfs
nodev   bdev
nodev   proc
nodev   cgroup
nodev   cgroup2
nodev   cpuset
nodev   devtmpfs
nodev   configfs
nodev   debugfs
nodev   tracefs
nodev   securityfs
nodev   sockfs
nodev   bpf
nodev   pipefs
nodev   ramfs
nodev   hugetlbfs
nodev   devpts
        ext3
        ext2
        ext4
        squashfs
        vfat
nodev   ecryptfs
        fuseblk
nodev   fuse
nodev   fusectl
nodev   efivarfs
nodev   mqueue
nodev   pstore
nodev   autofs
nodev   rpc_pipefs
nodev   binfmt_misc
nodev   overlay
nodev   aufs

總結

今天我們介紹了 VFS 的各種操作介面 (file_operations, inode_operations, dentry_operations),並介紹了 super block 還有檔案系統驅動。


上一篇
虛擬檔案系統 VFS (1)
下一篇
虛擬檔案系統 VFS (3)
系列文
Linux Kernel 網路巡禮14
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言